package com.lu.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.lu.model.constant.RedisKeyConstants;
import com.lu.model.entity.SysUser;
import com.lu.model.domain.LoginUser;
import com.lu.model.exception.LuBootAssert;
import com.lu.model.exception.InvalidTokenException;
import com.lu.model.response.Result;
import com.lu.service.SysUserService;
import com.lu.utils.JwtUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 验证用户密码正确后，生成一哥token，并将token返回给客户端 - 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法
 * - attemptAuthentication ：接收并解析用户凭证。 - successfulAuthentication ：用户成功登录后，这个方法会被调用，我们在这个方法里生成token。
 * @Author: zhanglu
 * @Date: 2022/7/26 15:35
 */
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

  private final AuthenticationManager authenticationManager;
  private final SysUserService userService;
  private final RedisTemplate<String, Object> redisTemplate;
  private final RedisTemplate<String, Object> redisTemplateStrategy;

  public JwtLoginFilter(AuthenticationManager authenticationManager, SysUserService userService,
      RedisTemplate<String, Object> redisTemplate,
      RedisTemplate<String, Object> redisTemplateStrategy) {
    this.authenticationManager = authenticationManager;
    this.userService = userService;
    this.redisTemplate = redisTemplate;
    this.redisTemplateStrategy = redisTemplateStrategy;
  }


  // 接收并解析用户凭证
  @Override
  public Authentication attemptAuthentication(HttpServletRequest req,
      HttpServletResponse res) throws AuthenticationException {
    String body = ServletUtil.getBody(req);
    LuBootAssert.isTrue(InvalidTokenException.class, ObjectUtil.isNotEmpty(body), "请输入账号密码");
    SysUser user = JSON.parseObject(body, SysUser.class);
    LoginUser loginUser = (LoginUser) userService.loadUserByUsername(user.getUsername());
    return authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(loginUser, user.getPassword(),
            loginUser.getAuthorities()));
  }

  // 用户成功登录后，这个方法会被调用，我们在这个方法里生成token
  @Override
  protected void successfulAuthentication(HttpServletRequest req,
      HttpServletResponse res,
      FilterChain chain,
      Authentication auth) throws IOException {
    LoginUser loginUser = (LoginUser) auth.getPrincipal();
    Long userId = loginUser.getUser().getId();
    String username = loginUser.getUser().getUsername();
    Object expiredTimeObj = redisTemplateStrategy.opsForHash()
        .get(RedisKeyConstants.SYS_USER_ROLE_RESOURCE_MANAGE_KEY,
            RedisKeyConstants.ACCESS_TOKEN_EXPIRED_TIME);
    long expiredTime = 60 * 60 * 24 * 7L;
    if (expiredTimeObj != null) {
      expiredTime = (Long) expiredTimeObj;
    }
    String accessToken = JwtUtils.generateToken(userId, username, expiredTime);
    redisTemplate.opsForValue()
        .set(RedisKeyConstants.loginUser(userId), JSON.toJSONString(loginUser), expiredTime,
            TimeUnit.SECONDS);
    res.setContentType("application/json; charset=utf-8");
    res.getWriter().write(JSON.toJSONString(Result.success()
            .params("token", accessToken).params("username", username)));
  }

}
